#include "config.h"

#include "ms2sim.h"

static sr_t	*sr_mod_conf;

static ECMBOOL
setup_sr_medias(sr_t *sr_medias)
{
	sr_t	*sr_media;

	SR_FOREACH_CHILD (sr_medias, NULL, sr_media) {
		if (!add_media(sr_media))
			return ECM_FALSE;
	}
	if (n_medias == 0) {
		ERR("media is required");
		return ECM_FALSE;
	}
	return ECM_TRUE;
}

static ECMBOOL
setup_sr_conf(sr_t *sr_conf)
{
	sr_t	*sr_duration;
	sr_t	*sr_arrival_rate;
	sr_t	*sr_skew;
	sr_t	*sr_cache_blksize;
	sr_t	*sr_n_caches;
	sr_t	*sr_cache_poolsize;
	sr_t	*sr_bcache_mod;
	sr_t	*sr_disk_blksize;
	sr_t	*sr_disk_BW;
	sr_t	*sr_disk_seektime;
	sr_t	*sr_disksim_param;
	sr_t	*sr_disksim_raid;
	sr_t	*sr_network_BW;
	sr_t	*sr_session_length_dist;
	sr_t	*sr_istime_dist;
	sr_t	*sr_logbasepath;
	sr_t	*sr_stat_from_start;
	sr_t	*sr_ignore_loss_violation_onstart;
	sr_t	*sr_allowed_loss_ratio;
	sr_t	*sr_disk_usage_trace;

	if ((sr_duration = sr_getc(sr_conf, "duration"))) {
		duration = msl_parse_duration((char *)sr_get_str(sr_duration));
		if (duration == 0) {
			ERR("duration: invalid format: %s", sr_get_str(sr_duration));
			return ECM_FALSE;
		}
	}

	if ((sr_arrival_rate = sr_getc(sr_conf, "arrival_rate"))) {
		if (!msl_parse_arrival_rate((char *)sr_get_str(sr_arrival_rate), &arrival_rate)) {
			ERR("arrival_rate: invalid format: %s", sr_get_str(sr_arrival_rate));
			return ECM_FALSE;
		}
	}

	if ((sr_skew = sr_getc(sr_conf, "skew"))) {
		skew = sr_get_dbl(sr_skew);
		if (skew > 1.0 || skew < 0) {
			ERR("skew: invalid skew value");
			return ECM_FALSE;
		}
	}

	if ((sr_cache_blksize = sr_getc(sr_conf, "cache_blksize"))) {
		char	*str = ecm_strdup(sr_get_str(sr_cache_blksize));
		ecm_uint64	blksize64;

		if (!msl_parse_bandwidth(str, &blksize64)) {
			ERR("cache_blksize: invalid cache block size");
			ecm_free(str);
			return ECM_FALSE;
		}
		ecm_free(str);
		cache_blksize = (ecm_uint32)blksize64;
	}

	if ((sr_n_caches = sr_getc(sr_conf, "n_caches"))) {
		n_caches = sr_get_int(sr_n_caches);
		if (n_caches == 0) {
			ERR("n_caches: invalid number of caches");
			return ECM_FALSE;
		}
	}

	if ((sr_cache_poolsize = sr_getc(sr_conf, "cache_poolsize"))) {
		ecm_uint64	poolsize;
		ecm_uint32	n_caches_poolsize;

		if (!msl_parse_bandwidth((char *)sr_get_str(sr_cache_poolsize), &poolsize)) {
			ERR("cache_poolsize: invalid format: %s", sr_get_str(sr_cache_poolsize));
			return ECM_FALSE;
		}
		n_caches_poolsize  = poolsize / cache_blksize;
		if (n_caches_poolsize > n_caches)
			n_caches = n_caches_poolsize;
	}

	if ((sr_bcache_mod = sr_getc(sr_conf, "bcache_mod"))) {
		bcache_mod = ecm_strdup(sr_get_str(sr_bcache_mod));
	}

	if ((sr_disk_blksize = sr_getc(sr_conf, "disk_blksize"))) {
		char	*str = ecm_strdup(sr_get_str(sr_disk_blksize));
		ecm_uint64	blksize64;

		if (!msl_parse_bandwidth(str, &blksize64)) {
			ERR("disk_blksize: invalid disk block size");
			ecm_free(str);
			return ECM_FALSE;
		}
		ecm_free(str);
		disk_blksize = (ecm_uint32)blksize64;
	}

	if ((sr_disk_BW = sr_getc(sr_conf, "disk_bandwidth"))) {
		if (!msl_parse_bandwidth((char *)sr_get_str(sr_disk_BW), &disk_BW)) {
			ERR("disk_bandwidth: invalid format: %s", sr_get_str(sr_disk_BW));
			return ECM_FALSE;
		}
	}

	if ((sr_disk_seektime = sr_getc(sr_conf, "disk_seektime"))) {
		disk_seektime = sr_get_int(sr_disk_seektime);
	}

	if ((sr_disksim_param = sr_getc(sr_conf, "disksim_param"))) {
		disksim_param = ecm_strdup(sr_get_str(sr_disksim_param));
	}

	if ((sr_disksim_raid = sr_getc(sr_conf, "disksim_raid"))) {
		disksim_raid = sr_get_int(sr_disksim_raid);
	}

	if ((sr_network_BW = sr_getc(sr_conf, "network_bandwidth"))) {
		if (!msl_parse_bandwidth((char *)sr_get_str(sr_network_BW), &network_BW)) {
			ERR("network_bandwidth: invalid format: %s", sr_get_str(sr_network_BW));
			return ECM_FALSE;
		}
	}

	if ((sr_session_length_dist = sr_getc(sr_conf, "session_length_dist"))) {
		sesslen_type = msl_sesslen_parse_typestr(sr_get_str(sr_session_length_dist));
		if (sesslen_type == MSL_SESSLEN_TYPE_NONE) {
			ERR("session_length_dist: invalid session length distribution");
			return ECM_FALSE;
		}
	}

	if ((sr_istime_dist = sr_getc(sr_conf, "interseek_time_dist"))) {
		istime_type = msl_istime_parse_typestr(sr_get_str(sr_istime_dist));
		if (istime_type == MSL_ISTIME_TYPE_NONE) {
			ERR("interseek_time_dist: invalid interseek time distribution");
			return ECM_FALSE;
		}
	}

	if ((sr_ignore_loss_violation_onstart = sr_getc(sr_conf, "ignore_loss_violation_onstart")))
		ignore_loss_violation_onstart = sr_get_bool(sr_ignore_loss_violation_onstart);

	if ((sr_allowed_loss_ratio = sr_getc(sr_conf, "allowed_loss_ratio")))
		allowed_loss_ratio = sr_get_dbl(sr_allowed_loss_ratio);

	if ((sr_logbasepath = sr_getc(sr_conf, "logbase"))) {
		const char	*logbase = sr_get_str(sr_logbasepath);
		if (ecm_empty_str(logbase)) {
			ERR("empty log base is not allowed");
			return ECM_FALSE;
		}
		logbasepath = ecm_strdup(logbase);
	}

	if ((sr_stat_from_start = sr_getc(sr_conf, "stat_from_start"))) {
		is_stat_from_start = sr_get_bool(sr_stat_from_start);
	}

	if ((sr_disk_usage_trace = sr_getc(sr_conf, "disk_usage_trace"))) {
		disk_usage_trace = ecm_strdup(sr_get_str(sr_disk_usage_trace));
	}

	if (!setup_sr_medias(sr_getc(sr_conf, "medias")))
		return ECM_FALSE;

	return ECM_TRUE;
}

static sr_t *
load_sr_conf(const char *confpath)
{
	sr_t	*sr;
	char	*buf;
	ecm_size_t	size;
	int	fd;

	fd = ecm_open(confpath, ECM_O_RDONLY);
	if (fd < 0)
		return NULL;
	if (!ecm_filesize(fd, &size)) {
		ecm_close(fd);
		return NULL;
	}
	buf = (char *)ecm_malloc(size + 1);
	if (buf == NULL) {
		ecm_close(fd);
		return NULL;
	}
	buf[size] = '\0';

	if (!ecm_read(fd, buf, &size)) {
		ecm_close(fd);
		return NULL;
	}
	sr = sr_asr_scan(buf);
	ecm_close(fd);

	return sr;
}

sr_t *
get_mod_sr_conf(const char *modname)
{
	return sr_getc(sr_mod_conf, modname);
}

ECMBOOL
init_conf_pre(void)
{
	sr_t	*sr_conf;
	ECMBOOL	res;

	if (ecm_empty_str(confpath) || !ecm_access(confpath, ECM_R_OK))
		return ECM_TRUE;

	sr_conf = load_sr_conf(confpath);

	if (sr_conf == NULL) {
		ERR("%s: cannot load configuration", confpath);
		return ECM_FALSE;
	}

	res = setup_sr_conf(sr_conf);
	sr_mod_conf = sr_dup(sr_getc(sr_conf, "modules"));

	sr_free(sr_conf);

	return res;
}
